package service

import (
	"GoMaestro/internal/dao"
	"GoMaestro/internal/entity"
	"GoMaestro/internal/entity/app"
	"GoMaestro/internal/strategy"
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
)

// PubServiceImp 发布服务接口默认实现体，可以操作默认的发布动作，如需不同的发布动作，可以实现新的接口配置新的参数
type PubServiceImp struct {
	App *app.App
	// 当前服务实现类不需要使用运行参数
	AppConfig *entity.AppConfig
	pipeline  *entity.Pipeline
	Dao       *dao.MysqlDao
}
type ValidationFunc func() error

func (p PubServiceImp) runValidations(validations []ValidationFunc) error {
	for _, validation := range validations {
		if err := validation(); err != nil {
			return err
		}
	}
	return nil
}

// 校验请求参数
func (p PubServiceImp) validatePublishingParams(params []string) error {
	if len(params) < 3 {
		return errors.New("参数不足，必须按顺序包含 env ,branch, action")
	}
	return nil
}

// 校验应用信息
func (p PubServiceImp) validateApp(appID int) (*app.App, error) {
	App, err := p.Dao.SelectAppByID(appID)
	if err != nil {
		return nil, fmt.Errorf("查询应用失败：%w", err)
	}
	return &App, nil
}

// 校验应用配置
func (p PubServiceImp) validateAppConfig(appID int, env string) (*entity.AppConfig, error) {
	appConfig, err := p.Dao.SelectAppConfigByAppIdAndEnv(appID, env)
	if err != nil {
		return nil, fmt.Errorf("查询应用配置失败：%w", err)
	}
	return &appConfig, nil
}

// 校验环境配置
func (p PubServiceImp) validateEnvConfig(env string) error {
	_, err := p.Dao.SelectEnvConfigByEnvName(env)
	if err != nil {
		return fmt.Errorf("查询环境配置失败：%w", err)
	}
	return nil
}

// 校验管线信息
func (p PubServiceImp) validatePipelines(language string) (*entity.Pipeline, error) {
	pipeline, err := p.Dao.SelectPipelinesByCodePackageType(language)
	if err != nil {
		return nil, fmt.Errorf("查询管线失败：%w", err)
	}
	return &pipeline, nil
}

// Publishing 默认发布动作,开发者对params按照顺序传入 env,branch, action即可发布
func (p PubServiceImp) Publishing(appID int, autoDePoly bool, params ...string) (int, *app.App, error) {
	// 责任链模式执行校验
	if err := p.runValidations([]ValidationFunc{
		func() error { return p.validatePublishingParams(params) },
		func() error {
			var err error
			p.App, err = p.validateApp(appID)
			return err
		},
		func() error {
			var err error
			_, err = p.validateAppConfig(p.App.AppID, params[0])
			return err
		},
		func() error { return p.validateEnvConfig(params[0]) },
		func() error {
			var err error
			p.pipeline, err = p.validatePipelines(p.App.Language)
			return err
		},
	}); err != nil {
		return 0, nil, err
	}
	err := p.GoStrategy(p.App, p.pipeline.JobName, params...)
	// 发布,同时得到参数策略，来获取参数的json字段
	taskID, defaultStrategy, err := p.App.Publish()
	if err != nil {
		return 0, nil, fmt.Errorf("发布失败：%w", err)
	}
	slog.Info("正在执行构建JOB:", slog.Any("JOB", p.pipeline.JobName), slog.Any("ID", taskID))
	// 记录发布任务
	taskRecord := &entity.TaskRecord{
		CiBuildID:  taskID,
		AppName:    p.App.AppName,
		Status:     entity.StatusPackaging,
		CiJobName:  p.pipeline.JobName,
		AutoDeploy: autoDePoly,
		CdJobName:  p.App.Specify,
	}

	if paramsJSON, err := json.Marshal(defaultStrategy.ToMap()); err != nil {
		return 0, nil, errors.New("已完成触发构建,但保存记录中JSON 编码失败")
	} else {
		taskRecord.PipelineParam = paramsJSON
	}

	go func() {
		err = p.Dao.InsertTask(taskRecord)
	}()
	return taskID, p.App, err
}

// GoStrategy 构造app并配置发布策略
func (p PubServiceImp) GoStrategy(a *app.App, jobName string, params ...string) error {
	if len(params) < 3 {
		return errors.New("参数不足，必须按顺序包含 env ,branch, action")
	}
	// 生成参数策略(假设该流水线参数只有env,branch)
	params1 := strategy.NewParamStrategy1(params[0], params[1], params[2])
	// 生成发布策略
	defaultStrategy := strategy.NewDefaultStrategy(jobName, params1)
	// 配置发布策略
	a.ProjectAction = defaultStrategy
	return nil
}
